﻿using CodeGenerator.CSharp;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Modifier = CodeGenerator.CSharp.Modifiers;

namespace CodeAngel.Domain
{
    public static class EntityCriteriaFileGenerator
    {
        #region CreateDtoExtensionsFileAsync

        /// <summary>
        /// Creates and writes to disk the Criteria file for the specified EntityDef.
        /// If this file already exists, it will be overwritten.
        /// </summary>
        public static Task CreateCriteriaFileAsync(this EntityDef thisED)
        {
            return Task.Factory.StartNew(() =>
            {
                EntityCriteriaFileGenerator.WriteToDisk(thisED);
            });
        }

        #endregion

        #region WriteToDisk

        /// <summary>
        /// Creates and writes to disk the DtoExtensions.cs file. If this file already exists, 
        /// it will be overwritten.
        /// </summary>
        private static void WriteToDisk(EntityDef thisED)
        {
            var ThisFB = new FileBuilder();
            ThisFB.ProjectName = thisED.Domain.ProjectName;
            ThisFB.Directory = thisED.EntitiesDirectoryPath;
            ThisFB.FileName = thisED.FileNames.Criteria();
            ThisFB.Namespace = thisED.CriteriaNamespace;
            ThisFB.IsAutoGenerated = true;

            // Add the using namespaces for this file.
            ThisFB.UsingNamespaces.Add("System");
            ThisFB.UsingNamespaces.Add(thisED.Domain.FrameworkNamespace);

            //Add the classes that make up this file.
            foreach (PropertyDef ThisPD in thisED.PropertyDefs.ScalarOrEntityProperties)
            {
                ThisFB.CodeUnits.Add(new CriterionClassBuilder(ThisPD));
            }

            // Write this file to disk.
            ThisFB.WriteToDisk(OverwriteExistingFile.Yes);
        }

        #endregion

        #region CriterionClassBuilder

        /// <summary>
        /// Builds the Criterion class for the identified PropertyDef.
        /// </summary>
        private class CriterionClassBuilder : ClassBuilder
        {
            #region Constructors

            public CriterionClassBuilder(PropertyDef thisPD)
                : base(thisPD.Names.Criterion(), Modifier.Public)
            {
                string BaseDT = (thisPD.IsEntityType ? "Guid" : thisPD.DataType);
                this.BaseClass = "Criterion<" + BaseDT + ">";

                // DefaultConstructor
                var DefaultConstructor = this.AddConstructor(thisPD.Names.Criterion(), Modifier.Public);
                DefaultConstructor.Base = string.Format("\"{0}\", \"{1}\", \"{2}\"", thisPD.EntityDef.TableName, thisPD.DatabaseColumnName, thisPD.FriendlyName);

                // ConstructorWithValue
                var ConstructorWithValue = this.AddConstructor(thisPD.Names.Criterion(), Modifier.Public);
                ConstructorWithValue.This = "";
                ConstructorWithValue.Documentation.Summary = "Creates this criterion object with its Value set to the identified value parameter and defaults the ComparisonOperator property to EqualTo.";
                ConstructorWithValue.Parameters.Add("value", BaseDT, "The value for this criterion.");
                ConstructorWithValue.Body.Add("this.SetValue(value);");
                ConstructorWithValue.Body.Add("this.ComparisonOperator = ComparisonOperators.EqualTo;");

                // Binary data type is an array of bytes, and VB.NET does not allow us to declare an
                // array variable with a type of another array.
                // TODO: Verify this issue exists in C#.
                if (thisPD.DataType != "Binary")
                {
                    // ConstructorWithValueAndComparisonOperator
                    var ConstructorWithValueAndComparisonOperator = this.AddConstructor(thisPD.Names.Criterion(), Modifier.Public);
                    ConstructorWithValueAndComparisonOperator.This = "";
                    ConstructorWithValueAndComparisonOperator.Documentation.Summary = "Creates this criterion object with its Value set to the identified value parameter and the ComparisonOperator property set to the identified comparisonOperator parameter.";
                    ConstructorWithValueAndComparisonOperator.Parameters.Add("value", BaseDT, "The value for this criterion.");
                    ConstructorWithValueAndComparisonOperator.Parameters.Add("comparisonOperator", "ComparisonOperator", "The comparison operator for this criterion.");
                    ConstructorWithValueAndComparisonOperator.Body.Add("this.SetValue(value);");
                    ConstructorWithValueAndComparisonOperator.Body.Add("this.ComparisonOperator = comparisonOperator;");
                }


            }

            #endregion

        }

        #endregion
    }
}
